package com.gitee.wsl.ext.map


fun <K, V> Map<K, V>.getOrDefault(index:K,default:V) = getOrElse(index){default}

fun <K, V> MutableMap<K, V>.putIfAbsent(index:K,default:V) = getOrPut(index){ default }

/**
 * Returns the value associated with the given key.
 *
 * @throws IllegalArgumentException if the key does not exist.
 * @throws IllegalStateException if the stored value is of the wrong type.
 */
fun < T : Any,K> Map<K,T>.requireGet(key: K): T {
    if (!containsKey(key)) {
        throw IllegalArgumentException("Bundle has no key $key")
    }
    @Suppress("DEPRECATION")
    return get(key)?: throw IllegalStateException("Wrong type found in Bundle for key $key")
}


inline fun <K, V> Map<out K, V>.firstOrNull(
    predicate: (Map.Entry<K, V>) -> Boolean
): V? {
    for (element in entries) {
        if (predicate(element)) {
            return element.value
        }
    }
    return null
}


/**
 * Find first element that was transformed with [transform] and its result is not null.
 *
 * If first is found, no other entry is mapped with [transform].
 */
inline fun <K, V, T> Map<out K, V>.firstMappingNotNullOrNull(transform: (Map.Entry<K, V>) -> T?): T? {
    for (element in this) {
        val transformResult = transform(element)
        if (transformResult != null) {
            return transformResult
        }
    }
    return null
}



/**
 * Finds the first map entry that matches the given predicate.
 *
 * This method iterates over the entries of the map and returns the first one that satisfies the predicate.
 * It is useful for finding specific entries based on custom criteria.
 *
 * @param predicate a function that evaluates each entry and returns true for the desired entry.
 * @return the first map entry that matches the predicate.
 * @throws NoSuchElementException if no entry matches the predicate.
 */
public inline fun <K, V> Map<K, V>.firstEntry(predicate: Map.Entry<K, V>.() -> Boolean) = entries.first(predicate)

/**
 * Finds the first map entry that matches the given predicate or returns null if none match.
 *
 * This method iterates over the entries of the map and returns the first one that satisfies the predicate,
 * or null if no entry matches. It is useful for safely finding specific entries without throwing exceptions.
 *
 * @param predicate a function that evaluates each entry and returns true for the desired entry.
 * @return the first map entry that matches the predicate, or null if no entry matches.
 */
public inline fun <K, V> Map<K, V>.firstEntryOrNull(predicate: Map.Entry<K, V>.() -> Boolean) =
    entries.firstOrNull(predicate)

/**
 * Finds the first key that matches the given predicate.
 *
 * This method iterates over the keys of the map and returns the first one that satisfies the predicate.
 * It is useful for finding specific keys based on custom criteria.
 *
 * @param predicate a function that evaluates each key and returns true for the desired key.
 * @return the first key that matches the predicate.
 * @throws NoSuchElementException if no key matches the predicate.
 */
public inline fun <K, V> Map<K, V>.firstKey(predicate: (K) -> Boolean) = keys.first(predicate)

/**
 * Finds the first key that matches the given predicate or returns null if none match.
 *
 * This method iterates over the keys of the map and returns the first one that satisfies the predicate,
 * or null if no key matches. It is useful for safely finding specific keys without throwing exceptions.
 *
 * @param predicate a function that evaluates each key and returns true for the desired key.
 * @return the first key that matches the predicate, or null if no key matches.
 */
public inline fun <K, V> Map<K, V>.firstKeyOrNull(predicate: (K) -> Boolean) = keys.firstOrNull(predicate)


/**
 * Finds the first value that matches the given predicate.
 *
 * This method iterates over the values of the map and returns the first one that satisfies the predicate.
 * It is useful for finding specific values based on custom criteria.
 *
 * @param predicate a function that evaluates each value and returns true for the desired value.
 * @return the first value that matches the predicate.
 * @throws NoSuchElementException if no value matches the predicate.
 */
public inline fun <K, V> Map<K, V>.firstValue(predicate: (V) -> Boolean) = values.first(predicate)

/**
 * Finds the first value that matches the given predicate or returns null if none match.
 *
 * This method iterates over the values of the map and returns the first one that satisfies the predicate,
 * or null if no value matches. It is useful for safely finding specific values without throwing exceptions.
 *
 * @param predicate a function that evaluates each value and returns true for the desired value.
 * @return the first value that matches the predicate, or null if no value matches.
 */
public inline fun <K, V> Map<K, V>.firstValueOrNull(predicate: (V) -> Boolean) = values.firstOrNull(predicate)


/**
 * Similar to [Map.all] but accept kotlin style lambda
 *
 * Could be very useful, when you need to pass existing function to [Map.all] but it requires function, that takes [Map.Entry], not [K] and [V]
 */
public inline fun <K, V> Map<K, V>.all(action: Map.Entry<K, V>.() -> Boolean): Boolean {
    for (entry in this) {
        if (!action(entry)) return false
    }
    return true
}

/**
 * Similar to [Map.any] but accept kotlin style lambda
 *
 * Could be very useful, when you need to pass existing function to [Map.any] but it requires function, that takes [Map.Entry], not [K] and [V]
 */
inline fun <K, V> Map<K, V>.any(action: Map.Entry<K, V>.() -> Boolean): Boolean {
    for (entry in this) {
        if (action(entry)) return true
    }
    return false
}

fun <K, V> Map<K, V>.getKeyByValue(value: V): K? {
    for ((key, value1) in this) {
        if (value == value1) return key
    }
    return null
}